6  Eccezioni

Non tutti gli errori presenti in un programma possono essere rilevati in compilazione.

Quando si verifica una condizione anormale in una sequenza di codice al momento dell’esecuzione si dice che si è verificata una eccezione.

Una condizione eccezionale è un problema che impedisce la regolare esecuzione del programma, ovvero l’elaborazione non può continuare perché non si hanno abbastanza informazioni per trattare il problema nel contesto corrente.

Per gestire una eccezione si potrebbe uscire dal contesto corrente relegando il tutto ad un contesto più alto. L’uscita dal contesto corrente corrisponde al sollevamento di un’eccezione.

Il punto opportuno da cui far riprendere l’esecuzione del programma si chiama exception handler. Esso ha il compito di recuperare la situazione in modo tale che il programma possa o cercare qualche linea di esecuzione alternativa o semplicemente continuare.

La sezione di codice che potrebbe generare delle eccezioni e che è seguita dal codice per il loro trattamento, è chiamata zona protetta.

Per lanciare una eccezione in modo esplicito si utilizza l’istruzione throw. La sua sintassi generale è la seguente:

throw ThrowableInstance;

dove ThrowableIstance è istanza della classe Throwable o istanza di una sua sottoclasse.

Esistono due costruttori per tutte le eccezioni standard:

Se un metodo solleva un’eccezione, questa deve essere in qualche modo catturata e trattata. Per catturare l’eccezione si può impostare un blocco speciale chiamato try block in cui si inserisce la sezione di codice che potrebbe generare l’eccezione. Poiché in un blocco try si potrebbero sollevare più eccezioni di tipo differente, al blocco try è possibile far seguire diverse clausole catch, una per ogni tipo di eccezione che si vuole gestire. Gli handler devono essere inseriti subito dopo il try block.

Quando viene sollevata una eccezione sono eseguiti i seguenti passi:

  1. Viene creato l’oggetto eccezione come un qualsiasi oggetto Java (nella memoria heap, con l’operatore new)

  2. Il flusso di esecuzione viene interrotto e il riferimento all’oggetto eccezione viene espulso dal contesto corrente

  3. Il meccanismo di gestione cerca il primo handler il cui tipo di argomento corrisponde con il tipo dell’eccezione sollevata;

  4. Entra nel blocco del rispettivo ‘catch’ e l’eccezione viene gestita.

  5. La ricerca degli handler termina quando il blocco ‘catch’ è eseguito.

Dopo la gestione dell’eccezione si passa ad eseguire la prima istruzione dopo l’ultimo blocco catch del try block da cui è stata espulsa l’eccezione.

La propagazione delle eccezioni continua finché l’eccezione non viene intercettata e gestita, oppure fino a quando non si esce dal metodo main, nel qual caso termina il programma e si produce il messaggio d’errore relativo all’eccezione.

È possibile anche pensare a un gestore che catturi qualsiasi eccezione. Per fare ciò si cattura la classe base delle eccezioni Exception.

L’istruzione try può avere una clausola finally opzionale: essa definisce una sezione di codice che viene eseguita indipendentemente da come si sia usciti dal blocco try.

Spesso viene usata per garantire il rilascio della memoria o di altre risorse a prescindere da quello che accade nel blocco t<ry.

Se non si verificano eccezioni, le istruzioni nella clausola finally vengono eseguite dopo che si è completato il blocco try.

Se si verifica un’eccezione nel blocco try, il controllo passa prima alla clausola catch appropriata e poi alla clausola finally.

Se un metodo può provocare una eccezione che non è in grado di gestire, deve generalmente specificare questo comportamento. A tale scopo si utilizza la clausola throws nella dichiarazione del metodo che elenca i tipi di eccezione che un metodo dovrebbe sollevare. Java garantisce la verifica in compilazione della corretta gestione delle eccezioni.

7 Eccezioni standard

Tutti i tipi di eccezione in Java sono derivati dalla classe Throwable.

Da Throwable sono derivate due sottoclassi che portano a due gerarchie di ereditarietà distinte.

La prima fa capo alla classe Exception utilizzata per le condizioni eccezionali che i programmi dovrebbero catturare. Da questa classe saranno poi derivate tutte le sottoclassi relative alle eccezioni personalizzate.

L’altro ramo della gerarchia è capeggiata dalla classe Error che definisce le eccezioni che normalmente un’applicazione non dovrebbe provare a catturare, poichè problemi anormali, ad esempio VirtualMachineError.

Java definisce diverse classi di eccezioni all’interno del package java.lang che viene implicitamente importato in tutti i programmi.

Le eccezioni più generali sono quelle derivate da RunTimeException. In Java queste eccezioni non devono essere inserite nell’elenco throws del metodo e sono definite eccezioni non controllate perché il compilatore non controlla se un metodo gestisce o lancia queste eccezioni. Un esempio di queste è la IndexOutOfBoundException.

Le eccezioni che non derivano da RunTimeException sono dette eccezioni controllate. Se un metodo espelle un’eccezione controllata e internamente non ha un handler, questo metodo è obbligato nella segnatura a dichiarare che può terminare con una eccezione di quel tipo. Un esempio di queste è la NoSuchMethodException.